#include <sys/types.h>		//socket、connect
#include <sys/socket.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>		//strerror
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <stdlib.h>
#include <getopt.h>		//getopt


#define MSG_STR "Hello LingYun IoT Studio Client\n"

void print_usage(char *progname)
{
	printf("%s usage: \n", progname);
	printf("-p(--port): sepcify server listen port\n");
	printf("-h(--help): print this help information\n");

	return ;
}

int main(int argc, char **argv)
{
	int						sockfd = -1;
	int						rv = -1;
	struct sockaddr_in		servaddr;		//ipv4用sockaddr_in，ipv6用sockaddr_in6
	struct sockaddr_in		cliaddr;		//ipv4用sockaddr_in，ipv6用sockaddr_in6
	socklen_t				len;
	int						port = 0;		//本机字节序
	char					buf[1024];
	int						clifd;
	int                     ch;
	int						on = 1;
	pid_t					pid;

	struct option 			opts[] = {
		{"port", required_argument, NULL, 'p'},			
		{"help", no_argument, NULL, 'h'},
		{NULL, 0, NULL, 0}
		
	};


	while( (ch = getopt_long(argc, argv, "p:h", opts, NULL)) != -1 )
	{
		switch(ch)
		{
			case 'p':
					port=atoi(optarg);
					break;

			case 'h':
					print_usage(argv[0]);
					return 0;

		}
	}


	if( !port )
	{
		print_usage(argv[0]);
		return 0;
	}


/*有了getopt函数进行命令行参数解析，可代替这一步
	if( argc < 3 )
	{
		printf("Program usage: %s [ServerIP] [port]\n", argv[0]);
		return -1;
	}

	servip=argv[1];		//字符串可以直接赋值
	port = atoi(argv[2]);		//整型不能直接赋值，要进行类型转换
*/


	sockfd=socket(AF_INET, SOCK_STREAM, 0);		//创建文字描述符,父进程
	if( sockfd < 0 )
	{
		printf("Create socket failure: %s\n", strerror(errno));
		return -1;		//一般出错返回负数，成功返回0
	}
	printf("Create socket[%d] successfully!\n", sockfd);
	
	setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));		//避免端口重复使用的报错,一定要在bind前调用，bind之后调用不生效

	memset(&servaddr, 0, sizeof(servaddr));
	servaddr.sin_family=AF_INET;
	servaddr.sin_port=htons(port);		//(网络字节序，大端字节序)主机字节顺序转换成网络字节顺序(把一个16位数高低位互换)
	servaddr.sin_addr.s_addr=htonl(INADDR_ANY);
	//	inet_aton("192.168.0.26", &servaddr.sin_addr);		//把ip地址进行转换，使其能够放在四字节空间中


	rv=bind(sockfd, (struct sockaddr *)&servaddr, sizeof(servaddr));
	if( rv < 0 )
	{
		printf("Socket[%d] bind on port[%d] failure: %s\n", sockfd, port, strerror(errno));
		return -2;
	}

	listen(sockfd, 13);
	printf("Start to listen on port [%d]!\n", port);



	while(1)
	{
		
		printf("Start accept new client incoming...\n");


		clifd=accept(sockfd, (struct sockaddr *)&cliaddr, &len);
		if( clifd < 0 )
		{
			printf("Accept new client failure: %s\n",strerror(errno));
			continue;
		}
		
		printf("Accept new client[%s:%d] successfully\n", inet_ntoa(cliaddr.sin_addr),ntohs(cliaddr.sin_port));
		
		
		pid = fork();
		if(pid < 0)
		{
			printf("fork() create child process failure: %S\n", strerror(errno));
			close(clifd);
			continue;
		}
		else if( pid > 0 )		//父进程，clifd给子进程处理，父进程用不着
		{
			close(clifd);
			continue;
		}
		else if( 0 == pid )			//子进程，用不着sockfd
		{
			char 		buf[1024];

			close(sockfd);
			printf("Child process start to communicate with socket client...\n\n");
			
			while(1)
			{
				memset(buf, 0, sizeof(buf));
				rv=read(clifd, buf, sizeof(buf));
				if( rv < 0 )
				{
					printf("Read data from client sockfd[%d] failure: %S\n", clifd, strerror(errno));
					close(clifd);
					//continue;
					//	break;
					exit(0);
				}
				else if( rv == 0 )
				{
					printf("Socket[%d] get disconnected\n", clifd);
					close(clifd);
					//break;
					exit(0);
				}
				else if( rv > 0 )
				{
					printf("Read %d bytes data from client:\n%s\n", rv, buf);
				}

				rv=write(clifd, MSG_STR, strlen(MSG_STR));
				if( rv < 0 )
				{
					printf("Write to client by sockfd[%d] failure: %s\n", sockfd, strerror(errno));
					close(clifd);
					//break;
					exit(0);
				}

				sleep(1);
			
			}//while结束

			printf("close client socket[%d] and child process exit\n", clifd);
			close(clifd);
			exit(0);


		}
	}

	close(sockfd);

	return 0;
	
}
